/**
 * @class L.Polyline
 * @aka Polyline
 */
L.Polyline.include({

    // @method intersects(): boolean
    // Check to see if this polyline has any linesegments that intersect.
    // NOTE: does not support detecting intersection for degenerate cases.
    intersects: function () {
        var points = this._getProjectedPoints(),
            len = points ? points.length : 0,
            i, p, p1;

        if (this._tooFewPointsForIntersection()) {
            return false;
        }

        for (i = len - 1; i >= 3; i--) {
            p = points[i - 1];
            p1 = points[i];


            if (this._lineSegmentsIntersectsRange(p, p1, i - 2)) {
                return true;
            }
        }

        return false;
    },

    // @method newLatLngIntersects(): boolean
    // Check for intersection if new latlng was added to this polyline.
    // NOTE: does not support detecting intersection for degenerate cases.
    newLatLngIntersects: function (latlng, skipFirst) {
        // Cannot check a polyline for intersecting lats/lngs when not added to the map
        if (!this._map) {
            return false;
        }

        return this.newPointIntersects(this._map.latLngToLayerPoint(latlng), skipFirst);
    },

    // @method newPointIntersects(): boolean
    // Check for intersection if new point was added to this polyline.
    // newPoint must be a layer point.
    // NOTE: does not support detecting intersection for degenerate cases.
    newPointIntersects: function (newPoint, skipFirst) {
        var points = this._getProjectedPoints(),
            len = points ? points.length : 0,
            lastPoint = points ? points[len - 1] : null,
            // The previous previous line segment. Previous line segment doesn't need testing.
            maxIndex = len - 2;

        if (this._tooFewPointsForIntersection(1)) {
            return false;
        }

        return this._lineSegmentsIntersectsRange(lastPoint, newPoint, maxIndex, skipFirst ? 1 : 0);
    },

    // Polylines with 2 sides can only intersect in cases where points are collinear (we don't support detecting these).
    // Cannot have intersection when < 3 line segments (< 4 points)
    _tooFewPointsForIntersection: function (extraPoints) {
        var points = this._getProjectedPoints(),
            len = points ? points.length : 0;
        // Increment length by extraPoints if present
        len += extraPoints || 0;

        return !points || len <= 3;
    },

    // Checks a line segment intersections with any line segments before its predecessor.
    // Don't need to check the predecessor as will never intersect.
    _lineSegmentsIntersectsRange: function (p, p1, maxIndex, minIndex) {
        var points = this._getProjectedPoints(),
            p2, p3;

        minIndex = minIndex || 0;

        // Check all previous line segments (beside the immediately previous) for intersections
        for (var j = maxIndex; j > minIndex; j--) {
            p2 = points[j - 1];
            p3 = points[j];

            if (L.LineUtil.segmentsIntersect(p, p1, p2, p3)) {
                return true;
            }
        }

        return false;
    },

    _getProjectedPoints: function () {
        if (!this._defaultShape) {
            return this._originalPoints;
        }
        var points = [],
            _shape = this._defaultShape();

        for (var i = 0; i < _shape.length; i++) {
            points.push(this._map.latLngToLayerPoint(_shape[i]));
        }
        return points;
    }
});